package com.sinai.mshab.client.formatting;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBoxBase;
import com.google.gwt.user.client.ui.Widget;

/**
 * This class remembers a specific visibility rule. A rule consists of three
 * things: (1) Source Widget, this widget is either visible or not (2) Trigger
 * widget, who's values or state influence the visibility of source widget (3)
 * Value, a condition of trigger widget under which source widget is shown or
 * not.
 * 
 * @author serge
 * 
 */
public class VisiblityRule {

	public static final String SHOW = "999";
	public static final String HIDE = "-999";

	private Widget original, trigger;
	private String condition;

	/**
	 * Makes a new rule.
	 * 
	 * @param original
	 *            the original widget
	 * @param trigger
	 *            the trigger widget
	 * @param condition
	 *            the condition of the trigger widget to fire off visibility
	 *            change
	 */
	public VisiblityRule(Widget original, Widget trigger, String condition) {
		this.original = original;
		this.trigger = trigger;
		this.condition = condition;

		// we can only really work with change listeners on the fly, everything
		// else will have to be directly called by someone, like after
		// populate()
		if (trigger instanceof CheckBox) {
			((CheckBox) trigger).addClickListener(new VisibilityListener());
		}
		if (trigger instanceof ListBox) {
			((ListBox) trigger).addChangeListener(new VisibilityListener());
		}
		if (trigger instanceof TextBoxBase) {
			((TextBoxBase) trigger).addClickListener(new VisibilityListener());
		}
	}

	/**
	 * Executes the current rule.
	 */
	public void execute() {

		// if the trigger is not blank
		if (!BlankChecker.isBlank(trigger)) {

			// handle simple SET rules
			if (condition.equals(SHOW)) {
				original.setVisible(true);
			} else if (condition.equals(HIDE)) {
				original.setVisible(false);
			}
			// these are regex value rules.
			else {
				handleRegexRules();
			}
		}
	}

	/**
	 * Ensures that the value of this widget validates against this widget's
	 * registered regex.
	 */
	private void handleRegexRules() {

		// handle listbox
		if (trigger instanceof ListBox) {
			ListBox list = (ListBox) trigger;
			if (list.getValue(list.getSelectedIndex()).matches(condition)) {
				original.setVisible(true);
			} else {
				original.setVisible(false);
			}
		}
		if (trigger instanceof HasText) {
			HasText widget = (HasText) trigger;
			if (widget.getText().matches(condition)) {
				original.setVisible(true);
			} else {
				original.setVisible(false);
			}
		}
	}

	/**
	 * Private class to handle visibility changes.
	 * 
	 * @author me
	 */
	private class VisibilityListener implements ClickListener, ChangeListener,
			KeyboardListener {
		public void onClick(Widget sender) {
			execute();
		}

		public void onChange(Widget sender) {
			execute();
		}

		public void onKeyDown(Widget sender, char keyCode, int modifiers) {
		}

		public void onKeyPress(Widget sender, char keyCode, int modifiers) {
		}

		public void onKeyUp(Widget sender, char keyCode, int modifiers) {
			execute();
		}
	}
}
